#ifndef _USerial_USerial_h
#define _USerial_USerial_h

#include <CtrlLib/CtrlLib.h>
#include <MyCodeEditor/CodeEditor.h>
#include "CSerialPort/include/CSerialPort/SerialPort.h"
#include "CSerialPort/include/CSerialPort/SerialPortInfo.h"
#include "MyEventQueue.hpp"
#include "Handler.hpp"
#include "MessageCtrl/MessageCtrl.h"
#include "GridLayoutCtrl/GridLayoutCtrl.h"

using namespace Upp;
using namespace itas109;

#define LAYOUTFILE <USerial/USerial.lay>
#include <CtrlCore/lay.h>

#define LLOG(x)   DLOG(x)
#define LDUMP(x)  DDUMP(x)


class MySerialListener: public CSerialPortListener
{
public:
	typedef MySerialListener CLASSNAME;
	MySerialListener()
	{
		MyRegisterEvent(EVENT_SERIAL_SEND_DATA, THISBACK(OnSendEvent));
		MyRegisterEvent(EVENT_OPEN_SERIAL, THISBACK(OnOpenEvent));
		MyRegisterEvent(EVENT_CLOSE_SERIAL, THISBACK(OnCloseEvent));
	}

	void onReadEvent(const char *portName, unsigned int readBufferLen);
	void OnSendEvent(const MyEvent &e);
	void OnOpenEvent(const MyEvent &e);
	void OnCloseEvent(const MyEvent &e);

};

class FilterDlg : public WithFilterLayout<TopWindow> {
public:
	typedef FilterDlg CLASSNAME;
	FilterDlg();
	~FilterDlg();

	void OnDataRecv(const MyEvent& e);
	void SetMainEditor(CodeEditor* mainDisplay) { m_mainDisplay = mainDisplay; }

private:
	void FullFilter();

	CodeEditor *m_mainDisplay;
	int last_line;

};

class SerialDlg: public WithSerialDispalyLayout<TopWindow> 
{
public:
	typedef SerialDlg CLASSNAME;
	SerialDlg();
	~SerialDlg();

	void OpenBtnClick();
	void SendBtnClick();
	void PortListClick();
	void BaudListClick();
	void OnSerialDataRecv(const MyEvent& e);
	void FreshPortList();	
	void OpenSerial(bool open);
	void OnHotPlug(const MyEvent& e);
	void OnPortOpened(const MyEvent& e);
	void OnPortClosed(const MyEvent& e);
	void OnQuit(const MyEvent& e);
	const String& GetPortName() { return portName; }
private:
	bool opened = false;
	bool forceAddTime = false;
	MyTime lastTime;
	String portName;
	FileStream logFile;

};

class DisplayElement
{
public:
	SerialDlg ui;
	CSerialPort serial;

	DisplayElement();
	~DisplayElement() {serial.close();}
};

class USerial : public WithUSerialLayout<TopWindow>, public CSerialPortHotPlugListener 
{
	friend class FilterDlg;

public:
	typedef USerial CLASSNAME;
	USerial();
	~USerial();

	void MainMenu(Bar& bar);
	void FileMenu(Bar& bar);
	void Quit();
	void OnSetLine(int line, const String& text);

	void OpenCloseFilterDlg() { if (m_filterDlg.IsOpen()) m_filterDlg.Close(); else m_filterDlg.Open(this); }
	void Vertical();
	void Horizontal();
	void NewSerialUI();
	void ProjectPath();

	// virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);

    void onHotPlugEvent(const char *portName, int isAdd);
	void OnMessageBottom(const MyEvent &e);

	void PutStatusBar(const String& text, bool temp = true) { if (temp) {statusBar.Temporary(text);} else {statusBar = text;} }

	//create a new code editor
	void CreateDisplay();

	CSerialPort* GetSerial(const char *portName) 
	{ 
		for (int i = 0; i < m_displays.GetCount(); i++)
		{
			if (!m_displays[i].serial.isOpen())
			{
				continue;
			}
			if (memcmp(m_displays[i].serial.getPortName(), portName, strlen(portName)) == 0)
			{
				return &m_displays[i].serial;
			}
		}
		return nullptr;
	}
	CSerialPort *GetUISerial(const String &portName) 
	{ 
		for (int i = 0; i < m_displays.GetCount(); i++)
		{
			// if (!m_displays[i].serial.isOpen())
			// {
			// 	continue;
			// }
			if (portName == m_displays[i].ui.GetPortName())
			{
				return &m_displays[i].serial;
			}
		}
		return nullptr;
	}

private:
	FilterDlg m_filterDlg;
	MessageCtrl m_msgCtrl;

	String m_openPortName;
	MySerialListener m_serialListener;
	
	//Vector of handler class instance
	Vector<MyEventHandler*> m_eventHandlers;
	Array<DisplayElement> m_displays;
};

void SetupAppPointer(USerial *app);
USerial *GetAppPointer();

#endif
